﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;

namespace nwrole.console
{
    public class Program : CommandBase
    {
        private static Regex CommandRegex = new Regex(@"^\s*([^ ]+) ?(.*?)\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline);
        private static Regex VariablesRegex = new Regex(@"\s*(?<key>[^:]+):(?<value>(""[^""]+"")|('[^']+')|([^ ]+))", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline);

        public static bool Running = true;
        public static Program Instance = new Program();

        static void Main(string[] args)
        {
            //if (args.Length > 1)
            //{
            //    Console.BufferWidth = Convert.ToInt32(args[0]);
            //    Console.WindowWidth = Convert.ToInt32(args[0]);
            //    Console.BufferHeight = 500;
            //    Console.WindowHeight = Convert.ToInt32(args[1]);
            //}
            //else
            //{
            //    Console.BufferWidth = 150;
            //    Console.WindowWidth = 150;
            //    Console.BufferHeight = 500;
            //    Console.WindowHeight = 50;
            //}

            Instance.MemberMain();
        }

        private List<Command> Commands = new List<Command>();

        public void MemberMain ()
        {
            foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
            {
                if (type.IsSubclassOf(typeof(CommandBase)))
                {
                    foreach (MethodInfo method in type.GetMethods())
                    {
                        CommandAttribute attr = (CommandAttribute)method.GetCustomAttributes(typeof(CommandAttribute), true).FirstOrDefault();
                        if (attr != null)
                        {
                            Command cmd = new Command();
                            cmd.Names = attr.Names.ToList();
                            cmd.Method = method;
                            cmd.ContainerType = type;
                            cmd.DefaultValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
                            cmd.IsGeneric = method.GetCustomAttributes(typeof(GenericCommandAttribute), true).FirstOrDefault() != null;

                            foreach (ParameterInfo parameter in method.GetParameters())
                            {
                                OptionalAttribute optional = (OptionalAttribute)parameter.GetCustomAttributes(typeof(OptionalAttribute), true).FirstOrDefault();
                                if (optional != null)
                                    cmd.DefaultValues.Add(parameter.Name, optional.Value);
                            }

                            List<string> removeNames = new List<string>();
                            foreach (var name in cmd.Names)
                            {
                                if ((from c in Commands where c.Names.Contains(name, StringComparer.OrdinalIgnoreCase) select c).FirstOrDefault() != null)
                                {
                                    this.PrintLine(ConsoleColor.Red, "Command name '{0}' already exists. Not including this name from {1}", name, type.Name);
                                    removeNames.Add(name);
                                }
                            }

                            foreach (var name in removeNames)
                                cmd.Names.Remove(name);

                            if (cmd.Names.Count > 0)
                                Commands.Add(cmd);
                        }
                    }
                }
            }

            this.PrintLine(ConsoleColor.Yellow, "### nwrole Debug Console ###");
            this.PrintLine(ConsoleColor.Yellow, "### Usage: cmd name:value name:\"value\" name:'value' ###");

            Running = true;
            while (Running)
            {
                Console.ForegroundColor = ConsoleColor.White;
                this.RunCommand(this.ReadLine("nwrole> ").Trim());
                Console.ForegroundColor = ConsoleColor.Gray;
            }
        }

        public void PrintUsage (Command command, string cmd)
        {
            this.Print(ConsoleColor.Red, "usage: {0}", cmd);
            foreach (ParameterInfo info in command.Method.GetParameters())
            {
                if (command.IsOptional(info.Name))
                    this.Print(ConsoleColor.Green, " [{0}:value]", info.Name);
                else
                    this.Print(ConsoleColor.Red, " {0}:value", info.Name);
            }
            this.PrintLine("");
        }

        public void RunCommand (string cccc)
        {
            string line = cccc.Trim();
            if (line.Length == 0)
                return;

            Match match = CommandRegex.Match(line);
            string cmd = match.Groups[1].Captures[0].Value.Trim();
            string parameters = match.Groups[2].Captures[0].Value.Trim();

            if (cmd.Equals("help", StringComparison.OrdinalIgnoreCase))
            {
                if (String.IsNullOrEmpty(parameters.Trim()))
                {
                    int ii = 1;
                    foreach (var c in Commands)
                    {
                        this.Print(ConsoleColor.Green, "   " + c.Names.First().PadRight(30));
                        if ((ii++) % 3 == 0)
                            this.PrintLine("");
                    }
                    if (ii % 3 == 0)
                        this.PrintLine("");
                }
                else
                {
                    var cc = (from c in Commands where c.Names.Contains(parameters.Trim(), StringComparer.OrdinalIgnoreCase) select c).FirstOrDefault();
                    if (cc == null)
                        this.PrintLine(ConsoleColor.Red, "help: command '{0}' not found", parameters.Trim());
                    else
                        this.PrintUsage(cc, parameters.Trim());
                }
                return;
            }

            Dictionary<string, object> @params = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

            foreach (Match m in VariablesRegex.Matches(parameters))
            {
                string key = m.Groups["key"].Captures[0].Value;
                string value = m.Groups["value"].Captures[0].Value;
                if (value.StartsWith("'") || value.StartsWith("\""))
                    value = value.Substring(1, value.Length - 2);
                @params.Add(key, value);
            }

            var command = (from c in Commands where c.Names.Contains(cmd, StringComparer.OrdinalIgnoreCase) select c).FirstOrDefault();
            if (command == null)
                this.PrintLine(ConsoleColor.Red, "{0}: command not found", cmd);
            else
            {
                try
                {
                    if (!command.Run(@params))
                    {
                        PrintUsage(command, cmd);
                    }
                }
                catch (Exception e)
                {
                    // this.PrintLine(ConsoleColor.DarkRed, "{0}: (error) {1}", cmd, e.Message);
                    this.PrintLine(ConsoleColor.DarkRed, "{0}: (error) {1}", cmd, e.ToString());
                }
            }
        }
    }
}

